package com.allwees.bs.module.pms.service.impl;

import com.allwees.bs.core.modelbase.branch.BranchUser;
import com.allwees.bs.core.modelbase.branch.EBranchType;
import com.allwees.bs.core.modelbase.exception.AuthException;
import com.allwees.bs.core.modelbase.exception.AuthExceptionEnum;
import com.allwees.bs.module.pms.entity.Category;
import com.allwees.bs.module.pms.entity.Product;
import com.allwees.bs.module.pms.entity.Sku;
import com.allwees.bs.module.pms.req.ApplyProductReq;
import com.allwees.bs.module.pms.req.DoEditProductReq;
import com.allwees.bs.module.pms.req.ProductInfoReq;
import com.allwees.bs.module.pms.req.SkuReq;
import com.allwees.bs.module.pms.service.MerchantProductService;
import com.allwees.bs.module.pms.util.ProductCodeUtil;
import com.allwees.core.common.constant.EStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 *
 *
 * @author yangylsky
 * @since 2019-08-21
 *
 */
@Slf4j
@Service
public abstract class MerchantProductServiceImpl extends BaseProductServiceImpl implements MerchantProductService {
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Product create(ProductInfoReq req) {
		Product product = req.toEntity();
		product.setStoreUuid(req.getStoreUuid());
		product.setEStatus(EStatus.CREATED);
		product.setScore(Product.DEFAULT_SCORE);
		product.attachRandomSales();
		if(StringUtils.isBlank(product.getNo())) {
			Category category = categoryService.findUniqueByField("no", product.getCategoryNo());
			product.setNo(genProductNo(category));
		}
		List<SkuReq> skus = req.getSkus();
		updateRetailPrice(product, skus);

		save(product);
		product.setDefNo(ProductCodeUtil.idToCode(product.getId()));
		updateById(product);

		skus.forEach(o -> {
			Sku sku = o.toSku();
			sku.setProductUuid(product.getUuid());
			sku.setProductNo(product.getValidNo());
			skuService.save(sku);
		});
		return product;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Product createAndApply(ProductInfoReq req) {
		Product product = create(req);
		apply(product, req.getBranchUserUuid());
		return product;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean edit(ProductInfoReq param) {
		String productUuid = param.getBo().getUuid();
		Assert.hasLength(productUuid, "product uuid cannot be blank");

		Product existProduct = findByUuid(productUuid);
		Assert.notNull(existProduct, "Product[" + productUuid + "] is null");
		log.info("===> product before edit:{}",existProduct);
		existProduct.assertStatus(EStatus.CREATED, EStatus.AUDIT_REJECTED, EStatus.PUTOFF_EDITING);

		//更新or保存sku信息
		List<SkuReq> skus = param.getSkus();
		String validNo = existProduct.getValidNo();
		if(CollectionUtils.isNotEmpty(skus)){
			Assert.isTrue(skus.stream().noneMatch(e -> StringUtils.isBlank(e.getColor())),"Sku should has a Color");
			Assert.isTrue(skus.stream().noneMatch(e -> StringUtils.isBlank(e.getSize())),"Sku should has a Size");
			List<Sku> skuList = skus
					.stream()
					.map(SkuReq::toSku)
					.peek(sku -> {
						sku.setProductUuid(productUuid);
						sku.setProductNo(validNo);
					}).collect(Collectors.toList());

			String[] skuUuids = skus.stream().map(Sku::getUuid).filter(StringUtils::isNotBlank).toArray(String[]::new);
			Map<String, Sku> skuMap = skuService.findByUuids(skuUuids).stream().collect(Collectors.toMap(Sku::getUuid, e -> e));

			Sku s;
			for (Sku sku : skuList) {
				s = skuMap.get(sku.getUuid());
				if(s == null){
					continue;
				}
				sku.setId(s.getId());
			}
			skuService.saveOrUpdateBatch(skuList);
		}

		//删除sku信息
		List<String> dels = param.getDels();
		if (CollectionUtils.isNotEmpty(dels)) {
			skuService.deleteByUuids(dels.toArray(new String[0]));
		}
		//更新产品信息
		updateRetailPrice(existProduct, skus);
		Product product = param.toEntity();
		product.setRetailPrice(existProduct.getRetailPrice());
		product.setOriginalPrice(existProduct.getOriginalPrice());
		product.setShippingPrice(existProduct.getShippingPrice());
		product.setUpdatedAt(new Date());
		log.info("===> product after edit:{}",product);
		return updateByUuid(product);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean readyToEdit(DoEditProductReq req) {
		Product existProduct = checkByUuid(req.getProductUuid());
		if(existProduct.isEditing()){
			return true;
		}
		Assert.isTrue(existProduct.canEdit(), "产品当前状态[" + existProduct.getEStatus() + "]不能编辑");
		Product product = new Product();
		product.setUuid(req.getProductUuid());
		product.setEStatus(EStatus.PUTOFF_EDITING);
		product.setUpdatedAt(new Date());
		return updateByUuid(product);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int apply(ApplyProductReq req) {
		List<Product> products = findByUuids(req.getProductUuids());
		String opUuid = req.getBranchUserUuid();
		int succcessNum = 0;
		for(Product product : products) {
			int skuCount = skuDao.getSkuCount(product.getUuid());
			Assert.isTrue(skuCount > 0, "商品关联的sku数量为0，不能提审");
			if(apply(product, opUuid)) {
				succcessNum++;
			}
		}
		return succcessNum;
	}

	protected abstract boolean apply(Product product, String opUuid);

	@Override
	@Transactional(rollbackFor = Exception.class)
	protected boolean puton(Product product, String operatorUuid) {
		product.assertStatus(EStatus.PUTOFF_ONESELF);
		product.setEStatus(EStatus.NORMAL);
		product.setUpdatedAt(new Date());
		setPublishInfo(product);
		return updateByUuid(product);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean putoff(Product product) {
		product.assertStatus(EStatus.NORMAL);
		product.setEStatus(EStatus.PUTOFF_ONESELF);
		product.setUpdatedAt(new Date());
		return updateByUuid(product);
	}

	/**
	 * 根据不同Merchant类型更新Product对应uuid值
	 *
	 */
	protected void setProductMerchantUuid(Product product, EBranchType branchType, BranchUser user) {
		product.setStoreUuid(user.getStoreUuid());
		switch(branchType) {
			case SUPPLIER:
				product.setSupplierUuid(user.getSupplierUuid());
				break;
			case RETAILER:
				product.setRetailerUuid(user.getRetailerUuid());
				break;
			case STORE:
				product.setStoreUuid(user.getStoreUuid());
				break;
			default:
				throw new AuthException(AuthExceptionEnum.WRONG_BRANCH_TYPE);
		}
	}
}
